import tkinter as tk
from tkinter import ttk, scrolledtext
import threading
import time
from datetime import datetime
import cv2
import numpy as np
from PIL import Image, ImageTk
import json
import os

class PasswordDialog(tk.Toplevel):
    """
    密码验证对话框类
    提供数字键盘输入密码功能
    """
    def __init__(self, parent, title="请输入密码"):
        super().__init__(parent)
        self.parent = parent
        self.title(title)
        self.geometry("300x400")
        self.resizable(False, False)
        self.transient(parent)  # 设置为主窗口的子窗口
        self.grab_set()  # 模态窗口
        
        self.password = ""  # 存储输入的密码
        self.result = False  # 验证结果
        self.correct_password = "681298"  # 正确密码
        
        # 创建界面元素
        self.create_widgets()
        
        # 居中显示
        self.update_idletasks()
        width = self.winfo_width()
        height = self.winfo_height()
        x = (self.parent.winfo_width() // 2) - (width // 2) + self.parent.winfo_x()
        y = (self.parent.winfo_height() // 2) - (height // 2) + self.parent.winfo_y()
        self.geometry(f"{width}x{height}+{x}+{y}")
    
    def create_widgets(self):
        """创建密码对话框的界面元素"""
        # 密码显示区域
        self.password_var = tk.StringVar(value="*****")
        password_frame = ttk.Frame(self, padding="20 10 20 10")
        password_frame.pack(fill=tk.X)
        
        password_label = ttk.Label(password_frame, textvariable=self.password_var, font=("Arial", 16))
        password_label.pack()
        
        # 数字键盘区域
        keypad_frame = ttk.Frame(self, padding="10")
        keypad_frame.pack(fill=tk.BOTH, expand=True)
        
        # 创建数字按钮
        buttons = [
            '1', '2', '3',
            '4', '5', '6',
            '7', '8', '9',
            '清空', '0', '删除'
        ]
        
        row, col = 0, 0
        for button_text in buttons:
            if button_text == '清空':
                btn = ttk.Button(keypad_frame, text=button_text, width=8, command=self.clear_password)
            elif button_text == '删除':
                btn = ttk.Button(keypad_frame, text=button_text, width=8, command=self.delete_char)
            else:
                btn = ttk.Button(keypad_frame, text=button_text, width=8, command=lambda text=button_text: self.add_char(text))
            
            btn.grid(row=row, column=col, padx=5, pady=5, sticky="nsew")
            
            col += 1
            if col > 2:
                col = 0
                row += 1
        
        # 设置网格权重使按钮均匀分布
        for i in range(4):
            keypad_frame.grid_rowconfigure(i, weight=1)
        for i in range(3):
            keypad_frame.grid_columnconfigure(i, weight=1)
        
        # 确定和取消按钮
        button_frame = ttk.Frame(self, padding="10")
        button_frame.pack(fill=tk.X)
        
        cancel_btn = ttk.Button(button_frame, text="取消", command=self.cancel)
        cancel_btn.pack(side=tk.RIGHT, padx=5)
        
        confirm_btn = ttk.Button(button_frame, text="确定", command=self.confirm)
        confirm_btn.pack(side=tk.RIGHT, padx=5)
    
    def add_char(self, char):
        """添加一个字符到密码"""
        if len(self.password) < 6:  # 限制密码长度为6位
            self.password += char
            self.update_password_display()
    
    def delete_char(self):
        """删除最后一个字符"""
        if self.password:
            self.password = self.password[:-1]
            self.update_password_display()
    
    def clear_password(self):
        """清空密码"""
        self.password = ""
        self.update_password_display()
    
    def update_password_display(self):
        """更新密码显示"""
        if self.password:
            display = '*' * len(self.password)
        else:
            display = "*****"
        self.password_var.set(display)
    
    def confirm(self):
        """确认按钮处理"""
        if self.password == self.correct_password:
            self.result = True
            self.destroy()
        else:
            self.password_var.set("密码错误!")
            self.after(1000, self.update_password_display)  # 1秒后恢复显示
    
    def cancel(self):
        """取消按钮处理"""
        self.result = False
        self.destroy()
    
    def show(self):
        """显示对话框并返回验证结果"""
        self.wait_window()
        return self.result

class QRDecodeVisualizer:
    """
    二维码解码可视化界面类
    用于实时展示二维码解码的状态、进度和结果
    """
    
    def __init__(self, root, qr_reconstructor=None):
        """
        初始化可视化界面
        
        参数:
            root: tkinter根窗口
            qr_reconstructor: QRCodeDataReconstructor实例，如果为None则仅启动界面
        """
        self.root = root
        self.root.title("中环信科数据传输工具")
        self.root.geometry("800x480")  # 适合7英寸屏幕的尺寸
        self.root.configure(bg="#f0f0f0")
        
        self.qr_reconstructor = qr_reconstructor
        self.is_decoding = False
        self.decoding_thread = None
        self.total_chunks = 100  # 默认总块数
        self.decoded_chunks = []
        self.task_hash = ""
        self.db_status = "待插入"  # 数据库状态：待插入、成功、失败
        self.db_error_reason = ""  # 数据库错误原因
        self.current_capture_interval = 1.0  # 默认扫码频率，将在start_decoding中从配置文件更新
        
        # 新增状态信息变量
        self.start_time = datetime.now()  # 程序开始时间
        self.total_transferred_data = 0  # 累计传输数据条数
        self.current_status = "就绪"  # 当前工作状态
        self.last_task_time = None  # 最新获取任务的时间
        self.last_scan_time = None  # 最后扫码时间
        
        # 初始化摄像头
        self.cap = None
        self.camera_index = -1
        self.camera_error = ""
        
        # 尝试多个摄像头索引
        for index in [0, 1, 2]:
            try:
                print(f"尝试初始化摄像头 {index}...")
                temp_cap = cv2.VideoCapture(index)
                
                # 添加延迟确保摄像头正确初始化
                time.sleep(0.5)
                
                if temp_cap.isOpened():
                    # 测试读取一帧
                    ret, frame = temp_cap.read()
                    if ret and frame is not None:
                        print(f"摄像头 {index} 初始化成功，能正常读取画面")
                        self.cap = temp_cap
                        self.camera_index = index
                        break
                    else:
                        print(f"摄像头 {index} 能打开但无法读取画面")
                        temp_cap.release()
                else:
                    print(f"无法打开摄像头 {index}")
                    temp_cap.release()
            except Exception as e:
                print(f"初始化摄像头 {index} 时出错: {e}")
                if 'temp_cap' in locals():
                    try:
                        temp_cap.release()
                    except:
                        pass
        
        if self.cap is None:
            self.camera_error = "无法找到可用的摄像头设备"
            print(f"警告: {self.camera_error}，将使用模拟画面")
        
        # 创建界面元素
        self.create_widgets()
        
        # 绑定窗口关闭事件
        self.root.protocol("WM_DELETE_WINDOW", self.on_closing)
        
        # 延迟300毫秒后自动开始解码，确保界面完全初始化
        self.root.after(300, self.start_decoding)
        
        # 初始状态更新 - 确保在create_widgets之后调用
        self.update_status("就绪")
        self.update_progress(0)
        
        # 启动运行时间更新
        self.update_run_time()
        
    def create_widgets(self):
        """创建所有界面控件 - 将界面划分为上下两个主要区域，并重新组织各个组件的位置"""
        # 上区域：主要工作区
        top_area = ttk.Frame(self.root, padding="0")  # 移除内边距
        top_area.pack(fill=tk.BOTH, expand=True, side=tk.TOP, pady=0)  # 移除垂直外边距
        
        # 为日志文本框配置错误标签样式
        self.log_text = None  # 先初始化变量
        
        # 上区域左侧：摄像头画面
        left_section = ttk.Frame(top_area, padding="5")
        left_section.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        
        # 摄像头标签框架
        self.camera_frame = ttk.LabelFrame(left_section, text="摄像头画面", padding=0)
        self.camera_frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
        
        # 创建一个容器
        self.camera_container = ttk.Frame(self.camera_frame)
        self.camera_container.pack(fill=tk.BOTH, expand=True)
        
        # 初始调整摄像头容器大小
        self.root.after(100, self._on_camera_frame_resize)
        
        # 摄像头显示标签
        self.camera_label = ttk.Label(self.camera_container, text="摄像头未启动", font=('Arial', 10))
        self.camera_label.pack(fill=tk.BOTH, expand=True)
        self.camera_label.config(anchor=tk.NW, justify=tk.LEFT, borderwidth=0, relief="flat")
        
        # 上区域右侧：logo、状态进度和任务信息
        right_section = ttk.Frame(top_area, width=350, padding="5")
        right_section.pack(side=tk.RIGHT, fill=tk.Y)
        right_section.pack_propagate(False)  # 固定宽度
        
        # 1. Logo区域
        logo_frame = ttk.Frame(right_section, padding="5")
        logo_frame.pack(fill=tk.X, padx=2, pady=3)
        
        try:
            # 尝试加载logo图像
            logo_path = os.path.join(os.path.dirname(__file__), "logo.png")
            if os.path.exists(logo_path):
                logo_img = Image.open(logo_path)
                # 调整logo大小以适应右侧区域
                logo_width, logo_height = 120, 48
                logo_img = logo_img.resize((logo_width, logo_height), Image.LANCZOS)
                self.logo_photo = ImageTk.PhotoImage(image=logo_img)
                self.logo_label = ttk.Label(logo_frame, image=self.logo_photo, borderwidth=0)
                self.logo_label.pack(pady=5, side=tk.TOP)
        except Exception as e:
            # 如果无法加载logo，不影响程序运行
            pass
        
        # 2. 状态与进度区域
        status_progress_frame = ttk.LabelFrame(right_section, text="进度")
        status_progress_frame.pack(fill=tk.X, padx=2, pady=3)
        
        # 进度条和进度标签
        progress_container = ttk.Frame(status_progress_frame)
        progress_container.pack(fill=tk.X, padx=3)
        
        self.progress_var = tk.DoubleVar(value=0)
        self.progress_bar = ttk.Progressbar(progress_container, variable=self.progress_var, length=250, mode='determinate')
        self.progress_bar.pack(side=tk.LEFT, fill=tk.X, expand=True, pady=2)
        
        self.progress_label = ttk.Label(progress_container, text="0/100 块", font=("Arial", 9))
        self.progress_label.pack(side=tk.RIGHT, padx=5, pady=2)
        
        # 3. 任务信息区域
        task_frame = ttk.LabelFrame(right_section, text="任务信息")
        task_frame.pack(fill=tk.BOTH, expand=True, padx=2, pady=3)
        
        # 已解码和总块数放在同一行
        ttk.Label(task_frame, text="已解码/总块数:", font=('Arial', 9)).grid(row=0, column=0, sticky=tk.W, padx=3, pady=1)
        self.decoded_chunks_var = tk.StringVar(value="0")
        self.total_chunks_var = tk.StringVar(value="100")
        # 创建一个显示已解码/总块数的标签
        self.chunk_info_label = ttk.Label(task_frame, text="0/100", font=('Arial', 9))
        self.chunk_info_label.grid(row=0, column=1, sticky=tk.W, padx=3, pady=1)
        
        
        ttk.Label(task_frame, text="数据库状态:", font=('Arial', 9)).grid(row=1, column=0, sticky=tk.W, padx=3, pady=1)
        self.db_status_var = tk.StringVar(value="待插入")
        self.db_status_label = ttk.Label(task_frame, textvariable=self.db_status_var, font=('Arial', 9))
        self.db_status_label.grid(row=1, column=1, sticky=tk.W, padx=3, pady=1)
        
        # 新增状态信息显示
        ttk.Label(task_frame, text="程序开始时间:", font=('Arial', 9)).grid(row=2, column=0, sticky=tk.W, padx=3, pady=1)
        self.start_time_var = tk.StringVar(value=self.start_time.strftime("%Y-%m-%d %H:%M:%S"))
        ttk.Label(task_frame, textvariable=self.start_time_var, font=('Arial', 8)).grid(row=2, column=1, sticky=tk.W, padx=3, pady=1)
        
        ttk.Label(task_frame, text="运行时间(小时):", font=('Arial', 9)).grid(row=3, column=0, sticky=tk.W, padx=3, pady=1)
        self.run_time_var = tk.StringVar(value="0.00")
        ttk.Label(task_frame, textvariable=self.run_time_var, font=('Arial', 9)).grid(row=3, column=1, sticky=tk.W, padx=3, pady=1)
        
        ttk.Label(task_frame, text="工作状态:", font=('Arial', 9)).grid(row=5, column=0, sticky=tk.W, padx=3, pady=1)
        self.work_status_var = tk.StringVar(value=self.current_status)
        ttk.Label(task_frame, textvariable=self.work_status_var, font=('Arial', 9)).grid(row=5, column=1, sticky=tk.W, padx=3, pady=1)
        
        ttk.Label(task_frame, text="累计的总块数:", font=('Arial', 9)).grid(row=6, column=0, sticky=tk.W, padx=3, pady=1)
        self.transferred_data_var = tk.StringVar(value="0")
        ttk.Label(task_frame, textvariable=self.transferred_data_var, font=('Arial', 9)).grid(row=6, column=1, sticky=tk.W, padx=3, pady=1)
        
        ttk.Label(task_frame, text="最新任务时间:", font=('Arial', 9)).grid(row=7, column=0, sticky=tk.W, padx=3, pady=1)
        self.last_task_time_var = tk.StringVar(value="- 无任务 -")
        ttk.Label(task_frame, textvariable=self.last_task_time_var, font=('Arial', 8)).grid(row=7, column=1, sticky=tk.W, padx=3, pady=1)
        
        # 下区域：日志
        bottom_area = ttk.Frame(self.root, padding="0")  # 移除内边距
        bottom_area.pack(fill=tk.BOTH, expand=False, side=tk.BOTTOM, pady=0)  # 移除垂直外边距
        
        # 日志框架
        log_frame = ttk.LabelFrame(bottom_area, text="日志", padding="0")
        log_frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=0)  # 仅保留水平内边距，移除垂直内边距
        
        # 设置日志区域 - 增高高度以显示更多日志内容
        self.log_text = scrolledtext.ScrolledText(log_frame, wrap=tk.WORD, font=('Courier New', 8), height=10)
        self.log_text.pack(fill=tk.BOTH, expand=True, padx=5, pady=0)  # 移除垂直内边距
        
        # 配置错误标签样式，用于红色文本显示
        self.log_text.tag_config("error", foreground="red")
        
        # 配置文本框不可编辑但可滚动
        self.log_text.config(state=tk.DISABLED)
    
    def toggle_decoding(self):
        """切换解码状态：开始或停止解码"""
        if self.is_decoding:
            # 停止解码需要密码验证
            password_dialog = PasswordDialog(self.root, title="请输入密码以停止解码")
            if password_dialog.show():
                self.stop_decoding()
        else:
            self.start_decoding()
    
    def start_decoding(self):
        """开始解码过程"""
        if not self.qr_reconstructor:
            self.log_message("错误: 未提供QRCodeDataReconstructor实例")
            return
        
        # 重置解码状态
        self.decoded_chunks = []
        self.db_status = "待插入"
        self.db_error_reason = ""
        
        # 从配置文件获取设置
        from config_manager import ConfigManager
        
        # 获取扫码频率
        capture_interval = ConfigManager.get_capture_interval()
        self.log_message(f"从配置文件读取扫码频率: {capture_interval} 秒")
        # 保存频率值供解码线程使用
        self.current_capture_interval = capture_interval
        
        # 获取并应用工作时间设置
        work_time_enabled, work_start_time, work_end_time = ConfigManager.get_work_time_settings()
        
        # 如果有QRCodeDataReconstructor实例，应用工作时间设置
        if self.qr_reconstructor:
            # 解析时间字符串
            time_parts = work_start_time.split(":")
            start_hour, start_minute = int(time_parts[0]), int(time_parts[1])
            
            time_parts = work_end_time.split(":")
            end_hour, end_minute = int(time_parts[0]), int(time_parts[1])
            
            # 应用工作时间设置
            self.qr_reconstructor.set_work_time(start_hour, start_minute, end_hour, end_minute)
            self.qr_reconstructor.enable_work_time_check(work_time_enabled)
            
            status = "启用" if work_time_enabled else "禁用"
            self.log_message(f"工作时间检查已{status}，工作时间: {work_start_time} - {work_end_time}")
        
        self.is_decoding = True
        # 自动模式，无需更新按钮状态
        self.update_status("解码中...")
        self.log_message("开始二维码解码过程")
        
        # 创建并启动解码线程
        self.decoding_thread = threading.Thread(target=self.decode_process, daemon=True)
        self.decoding_thread.start()
        
    # simulate_camera_feed方法已移除，不再使用模拟摄像头功能
    
    def stop_decoding(self):
        """停止解码过程"""
        self.is_decoding = False
        # 自动模式，无需更新按钮状态
        self.update_status("已停止")
        self.log_message("解码过程已停止")
        
        if self.decoding_thread and self.decoding_thread.is_alive():
            self.decoding_thread.join(timeout=1.0)
    
    def update_task_info(self):
        """更新任务信息 - 确保在主线程中执行所有UI更新"""
        # 所有UI更新都使用lambda函数包装，确保在主线程中执行
        def update_ui():
            # 更新已解码块数
            self.decoded_chunks_var.set(str(len(self.decoded_chunks)))
            # 更新总块数
            self.total_chunks_var.set(str(self.total_chunks))
            # 更新已解码/总块数标签
            self.chunk_info_label.config(text=f"{len(self.decoded_chunks)}/{self.total_chunks}")
            # 确保变量是字符串类型
            self.transferred_data_var.set(str(self.total_transferred_data))
            # 添加调试日志
            self.log_message(f"UI更新: 累计传输数据 = {self.total_transferred_data}")
            # 更新工作状态显示
            self.work_status_var.set(self.current_status)
            # 更新最新任务时间显示 - 这是关键的修复点
            if self.last_task_time is not None:
                self.last_task_time_var.set(self.last_task_time.strftime("%Y-%m-%d %H:%M:%S"))
            else:
                self.last_task_time_var.set("- 无任务 -")
            
            # 更新数据库状态显示
            if self.db_status == "成功插入":
                self.db_status_var.set("成功插入")
                self.db_status_label.config(foreground="green")
            elif self.db_status == "失败":
                # 如果有错误原因，显示失败及原因
                display_text = f"失败: {self.db_error_reason}" if self.db_error_reason else "失败"
                self.db_status_var.set(display_text)
                self.db_status_label.config(foreground="red")
            else:
                self.db_status_var.set("待插入")
                self.db_status_label.config(foreground="black")
        
        # 确保在主线程中执行UI更新
        self.root.after(0, update_ui)

    def decode_process(self):
        """二维码检测的主要循环"""
        self.is_decoding = True
        # 移除对不存在按钮的引用
        self.current_status = "解码中"
        self.root.after(0, lambda: self.work_status_var.set(self.current_status))
        
        try:
            # 开始解码循环
            while self.is_decoding:
                # 确保摄像头已打开
                if not hasattr(self, 'cap') or self.cap is None or not self.cap.isOpened():
                    self.root.after(0, lambda: self.log_message("警告: 摄像头未打开或不可用"))
                    time.sleep(2)  # 等待2秒后重试
                    continue
                
                try:
                    # 读取摄像头画面
                    ret, frame = self.cap.read()
                    if not ret or frame is None:
                        self.root.after(0, lambda: self.log_message("警告: 无法读取摄像头画面"))
                        time.sleep(2)  # 等待2秒后重试
                        continue
                    
                    # 更新摄像头画面显示
                    self.root.after(0, self.update_camera_frame)
                    
                    # 处理解码逻辑
                    if self.qr_reconstructor:
                        try:
                            # 尝试解码二维码
                            # 注意：decode_qr_from_frame方法内部应捕获可能的异常
                            result = self.qr_reconstructor.decode_qr_from_frame(frame)
                            
                            if result and isinstance(result, dict):
                                # 更新最后扫码时间
                                self.last_scan_time = datetime.now()
                                
                                # 获取必要的数据
                                chunk_id = result.get('chunk_id', '未知块')
                                total_chunks = result.get('total_chunks', 100)
                                task_hash = result.get('hash', '')
                                
                                # 更新总块数
                                if total_chunks > 0:
                                    self.total_chunks = total_chunks
                                
                                # 直接更新任务时间，不依赖于chunk_id的变化
                                if task_hash:
                                    # 添加详细日志用于调试
                                    self.root.after(0, lambda th=task_hash: self.log_message(f"扫描到任务哈希: {th}"))
                                    # 不检查是否变化，直接调用update_task_time让它内部处理
                                    self.root.after(0, lambda th=task_hash: self.update_task_time(th))
                                
                                # 添加到已解码块列表（去重）
                                if chunk_id not in self.decoded_chunks:
                                    self.decoded_chunks.append(chunk_id)
                                    self.decoded_chunks.sort()
                                      
                                    # 注释掉解码时的计数器更新，改为在数据库插入成功后更新
                                    # self.total_transferred_data += 1
                                    # self.root.after(0, lambda cnt=self.total_transferred_data: self.log_message(f"累计解码二维码个数更新为: {cnt}"))
                                         
                                    # 更新进度
                                    if self.total_chunks > 0:
                                        progress = len(self.decoded_chunks) / self.total_chunks * 100
                                        self.root.after(0, lambda p=progress: self.update_progress(p))
                                        self.root.after(0, lambda c=chunk_id, t=self.total_chunks: self.log_message(f"已解码块 {c}/{t}"))
                                            
                                        # 定期更新任务信息
                                        self.root.after(0, self.update_task_info)
                                            
                                        # 如果已接收所有块，更新状态
                                        if len(self.decoded_chunks) >= self.total_chunks:
                                            self.current_status = "处理数据中"
                                            self.root.after(0, lambda: self.work_status_var.set(self.current_status))
                                            self.root.after(0, self.update_task_info)
                        except json.JSONDecodeError:
                            self.root.after(0, lambda: self.log_message("警告: 无法解析二维码内容为JSON格式"))
                        except Exception as e:
                            self.root.after(0, lambda err=str(e): self.log_message(f"二维码检测过程出错: {err}"))
                except Exception as inner_error:
                    self.root.after(0, lambda err=str(inner_error): self.log_message(f"摄像头读取过程出错: {err}"))
                
                time.sleep(self.current_capture_interval)  # 使用配置的扫码频率
        except Exception as e:
            error_message = f"解码过程出错: {str(e)}"
            self.root.after(0, lambda: self.log_message(error_message))
            self.root.after(0, lambda: self.update_status("错误"))
            self.current_status = "错误"
        finally:
            if self.is_decoding:
                self.is_decoding = False
                # 移除对不存在按钮的引用
            
            # 重置状态为就绪
            if not self.is_decoding:
                self.current_status = "就绪"
                self.root.after(0, lambda: self.work_status_var.set(self.current_status))
                # 更新任务信息
                self.root.after(0, self.update_task_info)
                
            self.root.after(0, lambda: self.work_status_var.set(self.current_status))
    
    def update_camera_frame(self):
            """更新摄像头画面，使用固定的窄尺寸 - 与_on_camera_frame_resize保持一致"""
            # 设置与_on_camera_frame_resize相同的固定尺寸
            fixed_width = 400
            fixed_height = 240
            
            # 初始化图像为None
            img = None
            width, height = fixed_width, fixed_height
            
            try:
                # 尝试从摄像头获取画面
                if hasattr(self, 'cap') and self.cap is not None:
                    try:
                        # 检查摄像头是否仍处于打开状态
                        if self.cap.isOpened():
                            # 尝试读取画面
                            # 注意：某些OpenCV版本不支持CAP_PROP_TIMEOUT
                            ret, frame = self.cap.read()
                            
                            if ret and frame is not None:
                                # 调整画面到固定尺寸
                                img = cv2.resize(frame, (fixed_width, fixed_height))
                            else:
                                # 如果无法获取画面，使用固定尺寸并显示错误信息
                                width, height = fixed_width, fixed_height
                                img = np.zeros((height, width, 3), dtype=np.uint8)
            
                        else:
                            # 摄像头已关闭，使用固定尺寸
                            width, height = fixed_width, fixed_height
                            img = np.zeros((height, width, 3), dtype=np.uint8)
                            # 尝试重新打开摄像头
                            try:
                                self.cap.open(self.camera_index)
                                print(f"尝试重新打开摄像头 {self.camera_index}")
                            except Exception as reopen_error:
                                print(f"重新打开摄像头失败: {reopen_error}")
                    except Exception as camera_error:
                        print(f"读取摄像头画面时出错: {camera_error}")
                        # 如果发生异常，使用固定尺寸
                        width, height = fixed_width, fixed_height
                        img = np.zeros((height, width, 3), dtype=np.uint8)
                        cv2.putText(img, f"摄像头读取错误: {str(camera_error)}", 
                                   (100, 250), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2)
                else:
                    # 如果没有摄像头，使用固定尺寸的空白画面
                    width, height = fixed_width, fixed_height
                    img = np.zeros((height, width, 3), dtype=np.uint8)
            except Exception as e:
                print(f"摄像头更新过程中发生错误: {e}")
                # 确保img是有效的
                if 'img' not in locals() or img is None:
                    width, height = fixed_width, fixed_height
                    img = np.zeros((height, width, 3), dtype=np.uint8)
                cv2.putText(img, f"错误: {str(e)}", 
                           (100, 300), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 0, 255), 2)
            finally:
                # 转换OpenCV图像格式为PIL格式
                img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
                img_pil = Image.fromarray(img_rgb)
                
                # 保持图像原始尺寸，不进行调整
                # 仅在图像尺寸无效时使用默认尺寸
                if width <= 0 or height <= 0:
                    width, height = 640, 480
                    img_pil = img_pil.resize((width, height), Image.LANCZOS)
                
                # 转换为Tkinter可用的图像
                img_tk = ImageTk.PhotoImage(image=img_pil)
            
            # 更新标签图像
            self.camera_label.config(image=img_tk)
            self.camera_label.image = img_tk  # 保持引用以防止垃圾回收
        
    def update_status(self, status):
        """更新状态文本和状态变量"""
        # 更新状态变量
        self.current_status = status
        # 记录日志信息
        print(f"状态更新: {status}")
        # 替换任务完成状态文本
        display_status = "任务完成，等待下一个任务中......" if status == "任务完成" else status
        # 记录状态变化
        self.log_message(f"状态: {display_status}")
        # 立即更新任务信息，以刷新状态显示
        self.update_task_info()
    
    def update_progress(self, value):
        """更新进度条"""
        self.progress_var.set(value)
        self.progress_label.config(text=f"{len(self.decoded_chunks)}/{self.total_chunks} 块")
    
    def _on_camera_frame_resize(self, event=None):
        """使用固定的窄尺寸设置摄像头容器，确保与图像尺寸一致"""
        # 使用与integrated_visualizer.py中相同的固定尺寸
        fixed_width, fixed_height = 400, 240  # 适合7英寸屏幕的固定尺寸
        
        # 设置容器尺寸与图像尺寸完全一致
        self.camera_container.config(width=fixed_width, height=fixed_height)
        # 同时调整父框架尺寸以适应容器
        self.camera_frame.config(width=fixed_width + 20, height=fixed_height + 40)  # 考虑标签和边框

    # 已在前面定义了update_task_info方法，此处删除重复定义
    
    def check_last_scan_time(self):
        """检查最后扫码时间，如果超过一天则显示红色报警日志"""
        current_time = datetime.now()
        
        # 如果从未扫码或最后扫码时间超过一天
        if self.last_scan_time is None:
            # 计算程序运行时间
            run_days = (current_time - self.start_time).days
            if run_days >= 1:
                self.root.after(0, lambda: self.log_message("警告: 程序启动后从未扫码到数据！", is_error=True))
        else:
            # 计算最后扫码时间到现在的天数
            days_since_last_scan = (current_time - self.last_scan_time).days
            if days_since_last_scan >= 1:
                self.root.after(0, lambda: self.log_message(f"警告: 最近{days_since_last_scan}天内没有扫码到数据！", is_error=True))

    def update_run_time(self):
        """更新运行时间并定期检查最后扫码时间"""
        current_time = datetime.now()
        run_time = (current_time - self.start_time).total_seconds() / 3600  # 转换为小时
        self.run_time_var.set(f"{run_time:.2f}")
        
        # 每小时检查一次最后扫码时间
        self.check_last_scan_time()
        
        # 继续定时更新，每10秒更新一次
        self.root.after(10000, self.update_run_time)
        
    def update_task_time(self, task_hash):
        """更新最新任务时间"""
        if task_hash:
            # 确保last_task_time有值，无论task_hash是否变化
            self.last_task_time = datetime.now()
            # 更新变量
            self.task_hash = task_hash
            # 立即更新UI显示
            self.root.after(0, self.update_task_info)
            # 记录日志
            self.root.after(0, lambda: self.log_message(f"更新任务时间: {self.last_task_time.strftime('%Y-%m-%d %H:%M:%S')}"))
        
    def log_message(self, message, is_error=False):
        """在日志区域添加消息，支持错误信息的红色显示"""
        timestamp = datetime.now().strftime("%H:%M:%S")
        log_entry = f"[{timestamp}] {message}\n"
        
        # 启用编辑模式，添加文本，然后禁用编辑模式
        self.log_text.config(state=tk.NORMAL)
        
        # 如果是错误信息，使用红色文本
        if is_error:
            self.log_text.insert(tk.END, log_entry, "error")
        else:
            self.log_text.insert(tk.END, log_entry)
        self.log_text.see(tk.END)  # 滚动到最新文本
        self.log_text.config(state=tk.DISABLED)

    def update_db_status(self, status, error_reason=""):
        """
        更新数据库状态
        
        参数:
            status: 数据库状态（待插入、成功、失败）
            error_reason: 错误原因（当状态为失败时提供）
        """
        # 转换状态文本
        if status == "成功":
            self.db_status = "成功插入"
            # 数据库插入成功后更新计数器
            self.total_transferred_data += 1
            # 添加调试日志
            self.root.after(0, lambda cnt=self.total_transferred_data: self.log_message(f"数据库插入成功，累计的总块数更新为: {cnt}"))
        else:
            self.db_status = status
        self.db_error_reason = error_reason
        self.root.after(0, self.update_task_info)
        
    
    
    def on_closing(self):
        """窗口关闭前的清理操作，需要密码验证"""
        # 弹出密码验证对话框
        password_dialog = PasswordDialog(self.root, title="请输入密码以关闭窗口")
        
        # 只有密码验证正确才允许关闭窗口
        if password_dialog.show():
            if self.is_decoding:
                self.stop_decoding()
            
            # 释放摄像头资源
            if self.cap is not None:
                try:
                    self.cap.release()
                    print("摄像头资源已释放")
                except Exception as e:
                    print(f"释放摄像头时出错: {e}")
            
            self.root.destroy()
        # 密码验证失败则不执行任何操作，窗口保持打开状态

# 测试主函数
if __name__ == "__main__":
    root = tk.Tk()
    
    try:
        # 尝试导入并创建QRCodeDataReconstructor实例
        from decode import QRCodeDataReconstructor
        reconstructor = QRCodeDataReconstructor()
        # 创建可视化器实例并传入reconstructor
        visualizer = QRDecodeVisualizer(root, reconstructor)
    except ImportError:
        # 如果无法导入，则创建一个空实例
        print("警告: 无法导入QRCodeDataReconstructor，仅启动界面")
        visualizer = QRDecodeVisualizer(root)
    
    # 启动主循环
    root.mainloop()

# 集成到现有decode.py的示例代码（单独保存为integrated_visualizer.py）
'''
import tkinter as tk
from decode import QRCodeDataReconstructor
from visualizer import QRDecodeVisualizer

def main():
    # 创建QRCodeDataReconstructor实例
    reconstructor = QRCodeDataReconstructor()
    
    # 创建Tkinter根窗口
    root = tk.Tk()
    
    # 创建可视化器并传入reconstructor实例
    visualizer = QRDecodeVisualizer(root, reconstructor)
    
    # 启动主循环
    root.mainloop()

if __name__ == "__main__":
    main()
'''